<?php
/**
 * Request Contract
 */

namespace request;

use support\Dictionary;

abstract class Request
{
    protected $method, $url, $apiMethodName, $params, $get, $post, $response, $statusCode, $message;
    protected $fieldMapping = [];   // 字段映射
    protected $fieldDict = [];   // 字段字典转化
    protected $requestOptions = [];
    protected $resultName = '';
    protected $otherResultNames = [];

    // auto run request
    public function run($options = []) {
        if($this->method == 'soap') {
            return $this->soapRun($options);
        } else {
            return $this->httpRun($options);
        }
    }

    // web service request
    public function soapRun($options = []) {
        $options = array_merge($this->requestOptions, $options);
        $httpClient = new SoapRequest();
        $httpClient->run($this, $options);
        return $this;
    }

    // http request
    public function httpRun($options = []) {
        $options = array_merge($this->requestOptions, $options);
        $httpClient = new HttpRequest();
        $httpClient->run($this, $options);
        return $this;
    }

    /**
     * field mapping
     * @param $data
     * @param int $dimension    // 数组维度
     * @return mixed
     */
    protected function fieldMapping($data, $dimension = 1) {
        if(!$this->fieldMapping || !$data) return $data;

        $_data = [];
        foreach($data as $key => $val) {
            if($dimension == 1 && isset($this->fieldMapping[$key])) {
                $_data[$this->fieldMapping[$key]] = $val;
            } elseif ($dimension == 2 && is_array($val)) {
                $_data[$key] = $this->fieldMapping($val);
            }
        }
        return $_data;
    }

    /**
     * field Dictionary Convert
     * @param $data
     * @param int $dimension    // 数组维度
     * @return mixed
     */
    protected function fieldDictConvert($data, $dimension = 1) {
        if(!$this->fieldDict || !$data) return $data;

        $_data = [];
        foreach($data as $key => $val) {
            if($dimension == 1 && isset($this->fieldDict[$key])) {
                if($this->fieldDict[$key] == 'datetime') {
                    $_data[$key] = $val ? date('Y-m-d H:i:s', $val) : '';
                } else {
                    $_data[$key] = Dictionary::get($this->fieldDict[$key], $val);
                }
            } elseif ($dimension == 1) {
                $_data[$key] = $val;
            } elseif ($dimension == 2 && is_array($val)) {
                $_data[$key] = $this->fieldDictConvert($val);
            }
        }
        return $_data;
    }

    /**
     * @return mixed
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * @param mixed $url
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * @param $params
     * @param int $dimension  数组维度
     * @return mixed
     */
    protected function requestTransfer($params, $dimension = 1) {
        if(is_object($params)) $params = $params->toArray();
        $params = $this->fieldMapping($params, $dimension);
        return $this->fieldDictConvert($params, $dimension);
    }

    /**
     * @return mixed
     */
    public function getParams()
    {
        return $this->requestTransfer($this->params);
    }

    /**
     * @param mixed $params
     */
    public function setParams($params)
    {
        $this->params = $params;
    }

    /**
     * @return mixed
     */
    public function getGet()
    {
        return $this->get;
    }

    /**
     * @param mixed $get
     */
    public function setGet($get)
    {
        $this->get = $get;
    }

    /**
     * @return mixed
     */
    public function getPost()
    {
        return $this->requestTransfer($this->post);
    }

    /**
     * @param mixed $post
     */
    public function setPost($post)
    {
        $this->post = $post;
    }

    /**
     * @return mixed
     */
    public function getResponse()
    {
        return $this->response;
    }

    /**
     * @param mixed $response
     */
    public function setResponse($response)
    {
        $response = preg_replace('/[\r\n]+/', '', $response);
        $this->response = json_decode($response, true);
        $this->checkResponse($this->response);
    }

    /**
     * valid response
     * @param $response
     * @throws RequestException
     */
    protected function checkResponse($response) {}

    /**
     * @return mixed
     */
    public function getMethod()
    {
        return $this->method;
    }

    /**
     * @param mixed $method
     */
    public function setMethod($method)
    {
        $this->method = $method;
    }

    /**
     * @return mixed
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }

    /**
     * @param mixed $statusCode
     */
    public function setStatusCode($statusCode)
    {
        $this->statusCode = $statusCode;
    }

    /**
     * @return mixed
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * @param mixed $message
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * @return mixed
     */
    public function getApiMethodName()
    {
        return $this->apiMethodName;
    }

    /**
     * 将xml转换为数组
     * @param string $xml:xml文件或字符串
     * @return array
     */
    function xmlToArray($xml){
        //考虑到xml文档中可能会包含<![CDATA[]]>标签，第三个参数设置为LIBXML_NOCDATA
        if (file_exists($xml)) {
            libxml_disable_entity_loader(false);
            $xml_string = simplexml_load_file($xml,'SimpleXMLElement', LIBXML_NOCDATA);
        }else{
            libxml_disable_entity_loader(true);
            $xml_string = simplexml_load_string($xml,'SimpleXMLElement', LIBXML_NOCDATA);
        }
        $result = json_decode(json_encode($xml_string),true);
        return $result;
    }

    /**
     * 将数组转换为xml
     * @param array $arr :数组
     * @param object $dom :Document对象，默认null即可
     * @param object $node :节点对象，默认null即可
     * @param string $root :根节点名称
     * @param bool $cdata :是否加入CDATA标签，默认为false
     * @return string
     */
    public function arrayToXml($arr,$dom=null,$node=null,$root='xml',$cdata=false){
        if (!$dom){
            $dom = new \DOMDocument('1.0','utf-8');
        }
        if(!$node){
            $node = $dom->createElement($root);
            $dom->appendChild($node);
        }
        foreach ($arr as $key=>$value){
            $child_node = $dom->createElement(is_string($key) ? $key : 'node');
            $node->appendChild($child_node);
            if (!is_array($value)){
                if (!$cdata) {
                    $data = $dom->createTextNode($value);
                }else{
                    $data = $dom->createCDATASection($value);
                }
                $child_node->appendChild($data);
            }else {
                $this->arrayToXml($value,$dom,$child_node,$root,$cdata);
            }
        }
        return preg_replace('/><([^\/])/im', ">\r\n<$1", $dom->saveXML());    // xml格式化
    }

    /**
     * @return array
     */
    public function getRequestOptions()
    {
        return $this->requestOptions;
    }

    /**
     * @param array $requestOptions
     */
    public function setRequestOptions($requestOptions)
    {
        $this->requestOptions = $requestOptions;
    }

    /**
     * @return string
     */
    public function getResultName()
    {
        return $this->getApiMethodName().'Result';
    }

    /**
     * @param string $resultName
     */
    public function setResultName($resultName)
    {
        $this->resultName = $resultName;
    }

    /**
     * @return array
     */
    public function getOtherResultNames()
    {
        return $this->otherResultNames;
    }

    /**
     * @param array $otherResultNames
     */
    public function setOtherResultNames($otherResultNames)
    {
        $this->otherResultNames = $otherResultNames;
    }
}